/**
**************************************************************************************
*Founder Effect                                                                      *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include "QuestImporter.h"
#include "EntityManager.h"
#include "EntityFactory.h"
#include "StateFactory.h"
#include "Scene.h"
#include "IEntity.h"
#include "Tile.h"

#include "../shared/StringUtil.h"
#include "../shared/geom.h"
#include "../shared/vector.h"
#include "../render/ViewManager.h"
#include "../render/EntityView.h"
#include "../render/Model.h"

#include "../Kernel.h"

extern Kernel* g_kernel;


/** Create the a scene from a .MAP file */
Scene* QuestImporter::CreateScene(const std::string &file)
{
    StringUtil util(file, '.');
    // get the name of the map
    if ( util.HasNext() )
        m_name = util.GetNext();

    Release();

    // parse out the define file
    ParseDefine();

    // allocate memory for the new scene
    Scene* scene = new Scene;

    type_Layer layer1, layer2;
    // parse the layers
    ParseLayers( LAYER_ONE, layer1, scene );
    ParseLayers( LAYER_TWO, layer2, scene );

    scene->Init(layer1, layer2);
    // TEMP --HACK-- register the current scene with
    // the entity manager, TODO - send an event for this
    EntityManager::GetInstance()->SetScene( scene );
    // now create the entities
    ParseEntities();

    // Now fill the tiles with events
    ParseEvents( scene );
    
	return scene;
}

/** Parse the .DFN file */
void QuestImporter::ParseDefine()
{
    std::string sfile = m_name + ".dfn";

    MFile file;
    // open stream
    if ( file.Open( sfile ) ) 
    {
        g_kernel->LPrintf( ("ERROR: Could not find: " + sfile).c_str() );
        return;
    }
    
	// get the version
	m_version = file.ReadLine();

    // state the version
    g_kernel->LPrintf( ("--Map version: "+ m_version).c_str() );

    // read the file
    while ( !file.IsEnd() )
    {
        // Read the Char association
        std::string type = file.ReadNext( ":" );
        // read the image file name
        std::string filename = file.ReadNext( ":" );
        // read the frame time
        int frameTime = file.ReadInt();
        // read the amount of frames
        int frames  = file.ReadInt();
        // read the row in the image file
        int row     = file.ReadInt();
        // read the col in the image file
        int col     = file.ReadInt();

        // load the image
        Image image = g_kernel->GetResource()->LoadImages( filename, row ,col );
        
        // create the tile
        Tile* tile = new Tile( image, frameTime, frames, row, col );

        // insert into the map
        m_tiles[ type ] = tile;
    }
    // close off the stream
    file.Close();

}


/** Parse .LY1 and .LY2 files -- The map layer */
void QuestImporter::ParseLayers(int layer, type_Layer &layers, Scene* scene )
{
    std::string sfile =  m_name + ((layer == LAYER_ONE) ? ".ly1" : ".ly2"); 
    MFile file;
    if ( file.Open( sfile ) ) 
    {
        g_kernel->LPrintf( ("ERROR: Could not find: "+ sfile).c_str() );
        return;
    }
    if ( layer == LAYER_ONE )
    {
        std::string sound = file.ReadLine();
        g_kernel->LPrintf( ("--Map loading sound resource: " + sound).c_str() );
        g_kernel->GetSound()->PlayMusic( sound );
    }
    // parse the layer
    Parse( layers, file, scene );

    file.Close();
}


/** Parse a layer file */
void QuestImporter::Parse( type_Layer &layer, MFile &file, Scene* scene  )
{
    // get the map dimensions
    int size_x = file.ReadInt();
    int size_y = file.ReadInt();

    scene->SetMaxX( size_x );
    scene->SetMaxY( size_y );

    // print dimensions
    g_kernel->LPrintf("--Map dimensions %5d by %5d", size_x, size_y);

    for ( int y = 0; y < size_y; y++ ) {
        std::vector<Tile*> subArray;
        for(int x = 0; x < size_x; x++ )
        {
            // read the next tile ref
            std::string next = file.ReadNext(":");

            // check to see if it is valid
            if ( m_tiles.find( next ) != m_tiles.end() )
                subArray.push_back( m_tiles[ next ] );
            else if ( next == "0" )
            {
                subArray.push_back( NULL );
                continue;
            }
            else {
                // if its not valid, spit out an error message
                g_kernel->LPrintf( ("**Layer Parsing Error in: "+ file.FileName()).c_str() ); 
                g_kernel->LPrintf("**On line: %4d %4d", x, y );
                return;
            }      
        }
        file.ReadLine();
        layer.push_back( subArray );
        subArray.clear();
    }
    file.Close();
}

/** Parse .EVT file -- The events on each tile */
void QuestImporter::ParseEvents(Scene* scene)
{
    std::string sfile = m_name + ".evt";

    MFile file;
    // open stream
    if ( file.Open( sfile ) ) 
    {
        g_kernel->LPrintf( ("ERROR: Could not find: "+ sfile).c_str() );
        return;
    }

    g_kernel->LPrintf( "--Parsing Tile Events..." );
    while( !file.IsEnd() )
    {
        // the location of the tile
        int tile_x = file.ReadInt();
        int tile_y = file.ReadInt();

        // read the event type
        //int event_type = file.ReadInt();      // not needed anymore

        /*--------------------------------
            Build the Event structure
        ---------------------------------*/

        // the location of the effected entity
        int location_x = file.ReadInt();
        int location_y = file.ReadInt();

        // get the destination point
        int point_x = file.ReadInt();
        int point_y = file.ReadInt();

        
        // get the damage
        int damage = file.ReadInt();

        // read wait time
        int waitTime   = file.ReadInt();
        // read the script function
        std::string script = file.ReadNext(":");
     
        g_kernel->LPrintf( "%4d %4d : %4d %4d : %4d %4d : %4d ", tile_x, tile_y, //event_type,
                                        location_x, location_y, point_x,point_y, damage );
        // now find the tile
        Tile* tile = scene->GetTile(1, tile_x, tile_y );

        // Set the destination
        int wx = 0; int wy = 0;
        scene->TileToWorld( point_x, point_y, wx, wy );

        Vector2f dest(wx, wy);
        Vector2f tile_location( tile_x, tile_y );

        // create the entity
        TileEvent e;
        e.destination   = dest;
        e.damage        = damage;
        e.type          = 0;          //event_type;
        e.effects       = NULL;     // TODO: Get an entity by point
        e.waitTime      = waitTime;
        e.script        = script;

        // add the event to the tile
        tile->AddEvent( tile_location, e );

    }

    file.Close();
}

/** Parse .ENTS file -- The Entities on a map */
void QuestImporter::ParseEntities()
{
    std::string sfile =  m_name + ".ents";

    MFile file;
    // open stream
    if ( file.Open( sfile ) )
    {
        g_kernel->LPrintf( ("ERROR: Could not find: "+ sfile).c_str() );
        return;
    }
    g_kernel->LPrintf( "---Parsing Entities..." );
    EntityManager* entMng = EntityManager::GetInstance();
    EntityFactory* entFactory = EntityFactory::GetInstance();

    while( !file.IsEnd() ) {
        EntityFactory::Chunk currentEntityPrefab;
        currentEntityPrefab.fileName = file.FileName();
        currentEntityPrefab.start = file.GetPosition();
        // read the entities type name
        std::string entityType = file.ReadNext(":");
        
        IEntity* ent = NULL;
        if ( entityType == "Player" ) 
        {
            ent = entFactory->CreatePlayer( entityType );
        } 
        else
        {
            ent = entFactory->CreateEntity( entityType );
            if ( !ent )
            {
                g_kernel->LPrintf( ("**Error parsing Entities in: " 
                                            + sfile + " could not create entity, falling on default type!").c_str() );
                ent = entFactory->CreateEntity( "BaseEntity" );
            }
        }

        ent->m_attributes.Put( "entity_type", entityType );

        // read the entities reference
        std::string entRef      = file.ReadNext(":");
        std::string entityName  = file.ReadNext(":");

        
        /*--------------------------------------------------
            Start reading properties
        ---------------------------------------------------*/

        //
        file.ReadNext("{");
        file.ReadLine();

        bool rightBrace = false;
        while( !file.IsEnd() )
        {
            std::string line = file.ReadLine();

            if ( line == "}" ) 
            {
                rightBrace = true;
                break;
            }
            StringUtil util( line, ':' );
            
            std::string propName, v1, v2;
            // get the name
            if ( util.HasNext() )
            {
                propName = util.GetNext();
            }
            // get the value
            if ( util.HasNext() )
            {
                v1 = util.GetNext();
            }
            // get the 2nd value if applicable
            if ( util.HasNext() )
            {
                v2 = util.GetNext();
            }

            if ( v2 != "" && util.IsNumber( v1) && util.IsNumber( v2 ) )
            {
                ent->m_attributes.Put( propName, util.ToFloat( v1 ), util.ToFloat( v2 )  );     // vector 
            } else if ( util.IsNumber( v1 ) ){
                ent->m_attributes.Put( propName, util.ToInt( v1 ) ); // integer
            } else {
                ent->m_attributes.Put( propName, v1 );   // string
            }
        }

        if ( !rightBrace )
        {
            g_kernel->LPrintf( ("**Error parsing Entities in: " + sfile + " missing a closing \'}\' ").c_str() );
            g_kernel->LPrintf( "**On byte: %d of %d", file.GetPosition(), file.Size() );
            delete ent;
            return;
        }

        // apply common attributes
        ent->Apply();
        entMng->RegisterEntity( ent, entityName );

    /*--------------------------------------------------------
        Now parse out the states of these entity
    ---------------------------------------------------------*/
        if ( ViewManager::GetInstance()->ParseView( ent, &file ) )
           return;
        ent->SetState( ENT_STATES[IDLE] );
        
        //// now we can add the ent def
        //m_entities[entRef] = ent;
        file.ReadLine();
        currentEntityPrefab.end = file.GetPosition();
        entFactory->AddToPrefabList( entRef, currentEntityPrefab );
    }
    file.Close();
}


/** Release resources */
void QuestImporter::Release() 
{
    type_Tiles::iterator it = m_tiles.begin();
    for(; it != m_tiles.end();it++)
    {
        if ( it->second )
            delete it->second;
    }
    m_tiles.clear();
    m_entities.clear();
}

QuestImporter::~QuestImporter(void)
{
    Release();
}


